/******************************************************************************
Project    : Real time rtsp server library base on live555
*******************************************************************************
File Name    :  <AmrRTimeFramedSource.cpp>:  amr real time framed source source file
Module Name  :  
Description  :  None
History      :
 No.   Ver.     Date        Designed by  Comment
-----+--------+----------+------------+---------------------------------------
 001   0.00.01  06/02/2015  <3G@LIURENHUA>   <amr real time framed source source file>
*******************************************************************************/
#include "AmrRTimeFramedSource.h"

/******************************************************************************
  macro define
 ******************************************************************************/
#define AMR_MAXFRM_SIZE				(512   )
#ifdef NOCMP_FRAME
#define FT_INVALID						(65535)
#endif /* NOCMP_FRAME */

/******************************************************************************
  static variables define
 ******************************************************************************/
#ifdef NOCMP_FRAME
static unsigned short const frameSize[16] = {
	12, 13, 15, 17,
	19, 20, 26, 31,
	5, FT_INVALID, FT_INVALID, FT_INVALID,
	FT_INVALID, FT_INVALID, FT_INVALID, 0
};

static unsigned short const frameSizeWideband[16] = {
	17, 23, 32, 36,
	40, 46, 50, 58,
	60, 5, FT_INVALID, FT_INVALID,
	FT_INVALID, FT_INVALID, 0, 0
};
#endif /* NOCMP_FRAME */

/******************************************************************************
  class functions define
 ******************************************************************************/
AmrRTimeFramedSource* AmrRTimeFramedSource::createNew(
							UsageEnvironment & env,
#ifdef LOCAL_SOCK_INPUT
							const char  *pcIdentity,
#endif /* LOCAL_SOCK_INPUT */
							const Boolean bWideBand,
							const u_int8_t ucChNum,
#ifdef LOCAL_SOCK_INPUT
							const u_int32_t unSBufSize,
							const u_int32_t unRBufSize
#else
							queue_t *pstQue
#endif /* LOCAL_SOCK_INPUT */
							)
{
	return new AmrRTimeFramedSource(env,
#ifdef LOCAL_SOCK_INPUT
							pcIdentity,
#endif /* LOCAL_SOCK_INPUT */
							bWideBand, 
							ucChNum,
#ifdef LOCAL_SOCK_INPUT
							unSBufSize, 
							unRBufSize
#else
							pstQue
#endif /* LOCAL_SOCK_INPUT */
							);
}

AmrRTimeFramedSource::AmrRTimeFramedSource(
							UsageEnvironment & env,
#ifdef LOCAL_SOCK_INPUT
							const char  *pcIdentity,
#endif /* LOCAL_SOCK_INPUT */
							const Boolean bWideBand,
							const u_int8_t ucChNum,
#ifdef LOCAL_SOCK_INPUT
							const u_int32_t unSBufSize,
							const u_int32_t unRBufSize
#else
							queue_t *pstQue
#endif /* LOCAL_SOCK_INPUT */
							) 
: AMRAudioSource(env, bWideBand, ucChNum)
{
#ifdef LOCAL_SOCK_INPUT
	/* Clear class members */
	pstServer = NULL;

	/* Initialize local udp socket with identity */
	if( (LSOCK_ERR_NOERR != 
		eInitLocalUdpSocket(&pstServer, pcIdentity, 
		LSOCK_TYPE_S, unSBufSize, unRBufSize)) ){
		printf("[%s]initialize local udp socket failed\n",
			__func__);
		return;
	}
#else
	m_que = pstQue;
#endif /* LOCAL_SOCK_INPUT */

	m_pucLocalBuffer = new u_int8_t[AMR_MAXFRM_SIZE];
	if( (NULL == m_pucLocalBuffer) )
	{
		printf("[%s] malloc local buffer failed\n",
			__func__);
#ifdef LOCAL_SOCK_INPUT
		vDInitLocalUdpSocket(pstServer);
		pstServer = NULL;
#endif /* LOCAL_SOCK_INPUT */
		return;
	}
#ifdef NOCMP_FRAME
	m_usWrite = 0;
#endif /* NOCMP_FRAME */
}

AmrRTimeFramedSource::~AmrRTimeFramedSource()
{
#ifdef LOCAL_SOCK_INPUT
	if( (NULL != pstServer) ){
		vDInitLocalUdpSocket(pstServer);
		pstServer = NULL;
	}
#endif /* LOCAL_SOCK_INPUT */

	if( (NULL != m_pucLocalBuffer) ){
    		delete[] m_pucLocalBuffer;
		m_pucLocalBuffer = NULL;
	}
}

void AmrRTimeFramedSource::doGetNextFrame()
{
#ifndef NOCMP_FRAME
	int read = AMR_MAXFRM_SIZE;
	fFrameSize = 0;

#ifdef LOCAL_SOCK_INPUT
	if( (LSOCK_ERR_NOERR == eRecvDataByLUdpSock(
		pstServer, m_pucLocalBuffer, &read)) ){
		if( (read <= 0) ){
			printf("[%s]no data = %d\n", __func__, read);
			fNumTruncatedBytes = 0;
			return;
		}
	}else{
		printf("[%s]receive data failed from local socket\n", 
			__func__);
		fNumTruncatedBytes = 0;
		return;
	}
#else
	AVPacket *pkt =  (AVPacket *)q_pop_tail(m_que);
	if( (NULL != pkt) ){
		if( (pkt->size <= read) ){
			fNumTruncatedBytes = 0;
			read = pkt->size;
		}else{
			fNumTruncatedBytes = pkt->size - read;
		}
		if( (NULL != pkt->data) ){
			memcpy(m_pucLocalBuffer, pkt->data, read);
			free(pkt->data);
		}
		free(pkt);
	}else{
		fNumTruncatedBytes = 0;
		return;
	}
#endif /* LOCAL_SOCK_INPUT */

	fLastFrameHeader = m_pucLocalBuffer[0];
	//rtsp_db_info("fLastFrameHeader=%02x", 
	//	(int)fLastFrameHeader);
	if( (read > 1) ){
		memcpy(fTo, 
			m_pucLocalBuffer+1, read-1);
		fFrameSize = read - 1;
	}
#ifdef LOCAL_SOCK_INPUT
	fNumTruncatedBytes = 0;
#endif /* LOCAL_SOCK_INPUT */

	/* Update presentation time with frame rate */
	if ( (fPresentationTime.tv_sec == 0 
	 	&& fPresentationTime.tv_usec == 0) ) {
	 	/* This is the first frame, so use the current time */
	 	gettimeofday(&fPresentationTime, NULL);
	}else{
		u_int32_t uSeconds = 
			fPresentationTime.tv_usec + 20000;
		fPresentationTime.tv_sec += (uSeconds / 1000000);
		fPresentationTime.tv_usec = (uSeconds %1000000);
	}
	fDurationInMicroseconds = 20000;
	
  	nextTask() = 
		envir().taskScheduler().scheduleDelayedTask(0,
		(TaskFunc*)FramedSource::afterGetting, this);	
#else
	volatile u_int16_t usFrmSize = 0;
	volatile u_int16_t usLeft = m_usWrite;
	
	fFrameSize = 0;
	/* Read a valid frame */
	while( (1) )
	{
		u_int8_t *pucData = 
			(u_int8_t *)m_pucLocalBuffer;
		rtsp_db_info("usLeft=%d", (int)usLeft);
		
		/* Check invalid frame header */
		while( (usLeft > 0) )
		{
			u_int8_t ucData = *pucData++;
			usLeft  = usLeft - 1;
			if( (0 == (ucData & 0x83)) )
			{
				u_int8_t ft=(ucData & 0x78) >> 3;
				usFrmSize = fIsWideband ? 
					frameSizeWideband[ft] : 
					frameSize[ft];
				if ( (usFrmSize == FT_INVALID) )
				{
					printf("[%s] Invalid FT field\n",
						__func__);
					usFrmSize = 0;
				}else{
					usFrmSize *= fNumChannels;
					fLastFrameHeader = ucData;
					break;
				}
			}
		}
		rtsp_db_info("usLeft=%d, usFrmSize=%d", 
			usLeft, (int)usFrmSize);
		
		/* Check reamain data size > frame size? */
		if( (0 == usFrmSize) || ((
			usFrmSize>0) && (usLeft < usFrmSize)) ) {
			int read = AMR_MAXFRM_SIZE - usLeft;
#ifdef LOCAL_SOCK_INPUT
			if( (LSOCK_ERR_NOERR == eRecvDataByLUdpSock(
				pstServer, m_pucLocalBuffer, &read)) ){
				if( (read <= 0) ){
					printf("[%s]no data = %d\n", __func__, read);
					fNumTruncatedBytes = 0;
					return;
				}
			}else{
				printf("[%s]receive data failed from local socket\n", 
					__func__);
				fNumTruncatedBytes = 0;
				return;
			}
#else
			AVPacket *pkt =  (AVPacket *)q_pop_tail(m_que);
			if( (NULL != pkt) ){
				if( (pkt->size <= read) ){
					read = pkt->size;
				}
				if( (NULL != pkt->data) ){
					memcpy(m_pucLocalBuffer, pkt->data, read);
					free(pkt->data);
				}
				free(pkt);
			}else{
				fNumTruncatedBytes = 0;
				return;
			}
#endif /* LOCAL_SOCK_INPUT */
			usLeft = usLeft + read;
			if( (0 == usFrmSize) ){
				continue;
			}
		}
		/* Re-setting write index of local buffer */
		m_usWrite = usLeft - usFrmSize;
		/* Check frame size > max frame size? */
		if( (usFrmSize > fMaxSize) ){
			fNumTruncatedBytes = usFrmSize - fMaxSize;
			usFrmSize = fMaxSize;
		}else{
			fNumTruncatedBytes = 0;
		}
		fFrameSize = usFrmSize;
		rtsp_db_info("m_usWrite=%d", m_usWrite);
		
		/* Copy data into frame buffer */
		if( (fFrameSize > 0) ){
			memcpy(fTo, pucData, fFrameSize);
		}
		if( (m_usWrite > 0) ){
			memcpy(m_pucLocalBuffer, 
				pucData + fFrameSize, m_usWrite);
		}

		/* Update presentation time with frame rate */
		if ( (fPresentationTime.tv_sec == 0 
		 	&& fPresentationTime.tv_usec == 0) ) {
		 	/* This is the first frame, so use the current time */
		 	gettimeofday(&fPresentationTime, NULL);
		}else{
			u_int32_t uSeconds = 
				fPresentationTime.tv_usec + 20000;
			fPresentationTime.tv_sec += (uSeconds / 1000000);
			fPresentationTime.tv_usec = (uSeconds %1000000);
		}
		fDurationInMicroseconds = 20000;
		
	  	nextTask() = 
			envir().taskScheduler().scheduleDelayedTask(0,
			(TaskFunc*)FramedSource::afterGetting, this);
		break;
	}
#endif /* NOCMP_FRAME */
}
